/*******************************************************************************
  * 文件：YLComm.c
  * 作者：djy
  * 版本：v1.0.0
  * 日期：2024-02-22
  * 说明：板级通讯组件
*******************************************************************************/
/* 头文件 *********************************************************************/
#include "YLComm.h"
#include "YLDeviceCtrl.h"
#include "Serial.h"
#include "ComFun.h"
#include "Debug.h"
#include "RunProtect.h"

/* 宏定义 *********************************************************************/
/* 类型定义 *******************************************************************/
/* 变量定义 *******************************************************************/
/* 函数声明 *******************************************************************/
/* 函数定义 *******************************************************************/

/*******************************************************************************
  * 函数名: YLCommFrameCheck
  * 功  能：接收报文校验
  * 参  数：U8 au8Data[]：待校验的数据
  *         U8 u8len：数据长度
  * 返回值：NULL:非法报文
  *         BoardFrame_ts *：合法报文地址
  * 说  明 ：无
*******************************************************************************/
static YLFrame_ts* YLCommFrameCheck(U8 au8Data[],U8 u8len)
{
    U8 u8Index;

    // 遍历所有数据
    for(u8Index = 0;u8Index < u8len;u8Index++)
    {
        // 寻找帧头
        if(au8Data[u8Index] == U8_YL_COMM_STX)
        {
            // 强制指针转换
            YLFrame_ts *psFrame = (YLFrame_ts *)(au8Data+u8Index);

            // 剩余数据量不足以组成最短的报文
            if((u8len - u8Index) < U8_YL_COMM_FRAME_LEN_MIN)
            {
                // 返回NULL
                return NULL;
            }
            
			// 炉头地址不对
			if(psFrame->au8Addr != U8_YL_COMM_CZ1_ADDR && psFrame->au8Addr != U8_YL_COMM_CZ2_ADDR)
			{
				// 继续遍历
				continue;
			}
			
			
			// 计算校验和
			U8 u8CheckSum = CheckSum((U8 *)psFrame,psFrame->u8FreamLen - 1);

            // 检查校验和
            if(u8CheckSum != psFrame->au8Data[psFrame->u8FreamLen - 4])
            {
                // 继续遍历
                continue;
            }

            // 校验通过，返回合法报文指针
            return psFrame;
        }
    }

    // 遍历完所有数据，仍未找到合法报文
    return NULL;
}

// 跃龙通道一通讯发送
void YLComm_Chn1SendFrame(U8 u8Addr, U8 au8Data[], U8 u8Len)
{
    // 地址参数检查是否非法
    if(u8Addr != U8_YL_COMM_CZ1_ADDR && u8Addr != U8_YL_COMM_CZ2_ADDR)
    {   
        return;
    }
    
    // 数据参数检查是否非法
    if(au8Data == NULL || u8Len == 0)
    {
        return;
    }
    
    U8 au8SendData[18]; 
    
    // 强制指针转换
    YLFrame_ts* psFrame = (YLFrame_ts*)au8SendData;
    
    // 组报文
    psFrame->u8STX = U8_YL_COMM_STX;
    psFrame->au8Addr = u8Addr;
    psFrame->u8FreamLen = u8Len + 4;
    
    // Copy待发送的数据
    memcpy(psFrame->au8Data,au8Data,u8Len);
    
    // 计算校验和
    U8 u8CheckSum = CheckSum((U8 *)psFrame,psFrame->u8FreamLen - 1);

    // 更新校验和
    psFrame->au8Data[psFrame->u8FreamLen - 4] =  u8CheckSum;
    
    // 发送数据
    SerialSendData(SERIAL_BOARD_CHN1,(U8 *)psFrame,psFrame->u8FreamLen);
}

// 跃龙通道二通讯发送
void YLComm_Chn2SendFrame(U8 u8Addr, U8 au8Data[], U8 u8Len)
{
    // 地址参数检查是否非法
    if(u8Addr != U8_YL_COMM_CZ1_ADDR && u8Addr != U8_YL_COMM_CZ2_ADDR)
    {   
        return;
    }
    
    // 数据参数检查是否非法
    if(au8Data == NULL || u8Len == 0)
    {
        return;
    }
    
    U8 au8SendData[18]; 
    
    // 强制指针转换
    YLFrame_ts* psFrame = (YLFrame_ts*)au8SendData;
    
    // 组报文
    psFrame->u8STX = U8_YL_COMM_STX;
    psFrame->au8Addr = u8Addr;
    psFrame->u8FreamLen = u8Len + 4;

    // Copy待发送的数据
    memcpy(psFrame->au8Data,au8Data,u8Len);
    
    // 计算校验和
    U8 u8CheckSum = CheckSum((U8 *)psFrame,psFrame->u8FreamLen - 1);

    // 更新校验和
    psFrame->au8Data[psFrame->u8FreamLen - 4] =  u8CheckSum;
    
    // 发送数据
    SerialSendData(SERIAL_BOARD_CHN2,(U8 *)psFrame,psFrame->u8FreamLen);
}

// 跃龙通讯解析任务 
void YLComm_RecvFrameTaskChn1(void)
{
    U8 au8RecvData[50];

    // 清空缓冲区
    memset(au8RecvData,0x00,sizeof(au8RecvData));
    
    // 将数据从串口接收缓冲区取出
    U8 u8RecvLen = SerialRxFIFOPeek(SERIAL_BOARD_CHN1,au8RecvData,sizeof(au8RecvData));

    if(u8RecvLen != 0)
    {
        // 报文合法性校验
        YLFrame_ts* psFrame = YLCommFrameCheck(au8RecvData,u8RecvLen);

        if(psFrame != NULL)
        {
            // 合法报文处理
//            DEBUG("Recv %d CZ frame:");
//            DEBUG_ARR(psFrame->au8Data,psFrame->u8FreamLen-4);

            // 添加接收正常，用于判断通信是否正常 


            // 引用外部变量，缓存设备数据
            extern YLCommDevInfor_ts sYLDevInfor;
            
            YLFrameRecvData_ts *psRecvDate = (YLFrameRecvData_ts *)psFrame->au8Data;
            
            // 根据炉头地址，缓存响应数据
            if(psFrame->au8Addr == U8_YL_COMM_CZ1_ADDR)
            {
                // 传递接收参数
                sYLDevInfor.u8RealPower[u8CZ1] = psRecvDate->RealPower;
                sYLDevInfor.u8AD_IGBTTemper[u8CZ1] = psRecvDate->IGBT_NTC_AD;
                sYLDevInfor.u8AD_CoilTemper[u8CZ1] = psRecvDate->Coil_NTC_AD;
                sYLDevInfor.u8AD_Voltage[u8CZ1] = psRecvDate->ACVoltageAD;
                sYLDevInfor.u8PanState[u8CZ1] = psRecvDate->PanPresent;
            }
            else if(psFrame->au8Addr == U8_YL_COMM_CZ2_ADDR)
            {
                // 传递接收参数
                sYLDevInfor.u8RealPower[u8CZ2] = psRecvDate->RealPower;
                sYLDevInfor.u8AD_IGBTTemper[u8CZ2] = psRecvDate->IGBT_NTC_AD;
                sYLDevInfor.u8AD_CoilTemper[u8CZ2] = psRecvDate->Coil_NTC_AD;
                sYLDevInfor.u8AD_Voltage[u8CZ2] = psRecvDate->ACVoltageAD;
                sYLDevInfor.u8PanState[u8CZ2] = psRecvDate->PanPresent;
            }    
        
            // 清空当前报文，以及当前报文之前的数据
            SerialRxFIFOClear(SERIAL_BOARD_CHN1,psFrame->u8FreamLen + ((U32)psFrame - (U32)au8RecvData));
        }
        else
        {
            // 非法报文且缓冲区数据长度超过25个字节
            if(u8RecvLen >= 64)
            {
                // 清空串口接收缓冲区，防止数据堆积 - 几乎没有进到此处的
                SerialRxFIFOClear(SERIAL_BOARD_CHN1,u8RecvLen);
            }
        }
    }
}

// 跃龙通讯解析任务 
void YLComm_RecvFrameTaskChn2(void)
{
    U8 au8RecvData[50];

    // 清空缓冲区
    memset(au8RecvData,0x00,sizeof(au8RecvData));
    
    // 将数据从串口接收缓冲区取出
    U8 u8RecvLen = SerialRxFIFOPeek(SERIAL_BOARD_CHN2,au8RecvData,sizeof(au8RecvData));

    if(u8RecvLen != 0)
    {
        // 报文合法性校验
        YLFrame_ts* psFrame = YLCommFrameCheck(au8RecvData,u8RecvLen);

        if(psFrame != NULL)
        {
            // 合法报文处理
//            DEBUG("Recv %d CZ frame:",psFrame->au8Addr);
//            DEBUG_ARR(psFrame->au8Data,psFrame->u8FreamLen-4);
            
            // 引用外部变量，缓存设备数据
            extern YLCommDevInfor_ts sYLDevInfor;
            
            YLFrameRecvData_ts *psRecvDate = (YLFrameRecvData_ts *)psFrame->au8Data;
            
            // 根据炉头地址，缓存响应数据 - 这里可能地址与炉头不对应
            if(psFrame->au8Addr == U8_YL_COMM_CZ1_ADDR)
            {
                // 传递接收参数
                sYLDevInfor.u8RealPower[u8CZ4] = psRecvDate->RealPower;
                sYLDevInfor.u8AD_IGBTTemper[u8CZ4] = psRecvDate->IGBT_NTC_AD;
                sYLDevInfor.u8AD_CoilTemper[u8CZ4] = psRecvDate->Coil_NTC_AD;
                sYLDevInfor.u8AD_Voltage[u8CZ4] = psRecvDate->ACVoltageAD;
                sYLDevInfor.u8PanState[u8CZ4] = psRecvDate->PanPresent;
            }
            else if(psFrame->au8Addr == U8_YL_COMM_CZ2_ADDR)
            {
                // 传递接收参数
                sYLDevInfor.u8RealPower[u8CZ3] = psRecvDate->RealPower;
                sYLDevInfor.u8AD_IGBTTemper[u8CZ3] = psRecvDate->IGBT_NTC_AD;
                sYLDevInfor.u8AD_CoilTemper[u8CZ3] = psRecvDate->Coil_NTC_AD;
                sYLDevInfor.u8AD_Voltage[u8CZ3] = psRecvDate->ACVoltageAD;
                sYLDevInfor.u8PanState[u8CZ3] = psRecvDate->PanPresent;
            }
            
            // 清空当前报文，以及当前报文之前的数据
            SerialRxFIFOClear(SERIAL_BOARD_CHN2,psFrame->u8FreamLen + ((U32)psFrame - (U32)au8RecvData));
        }
        else
        {
            // 非法报文且缓冲区数据长度超过25个字节
            if(u8RecvLen >= 64)
            {
                // 清空串口接收缓冲区，防止数据堆积 - 几乎没有进到此处的
                SerialRxFIFOClear(SERIAL_BOARD_CHN2,u8RecvLen);
            }
        }
    }
}

// 初始化
void YLComm_Init(void)
{	
	// 创建解析任务：通道1，左侧功率板
    OS_TaskCreate(YLComm_RecvFrameTaskChn1);
    
    // 创建解析任务：通道2，右测功率板
    OS_TaskCreate(YLComm_RecvFrameTaskChn2);  

    // 负载控制初始化
    YLComm_DevCtrlInit();
}

/***************************** 文件结束 ***************************************/
